feat: add cueapi messages command group (messaging primitive — message lifecycle)#29
Conversation
govindkavaturi-art
left a comment
There was a problem hiding this comment.
Clean port of messages lifecycle (send/get/read/ack). Slug-form addressing handled via --from/--to with X-Cueapi-From-Agent header. --priority clamped via click.IntRange(1, 5) matches server semantics. Approve.
|
@mikemolinet — this PR is approved but has conflict with main since #28 ( Action: rebase onto main, keep both class definitions / both command groups. git fetch origin main
git rebase origin/main
# In tests/test_cli.py: keep BOTH _AgentsClient AND your test class
# In cueapi/cli.py: keep BOTH the agents group from #28 AND your additions
git add -u
git rebase --continue
git push --force-with-leaseI'll re-merge as soon as it's green. Sorry for the cascade — it's the cost of merging the agents group first; the rest of your stack just needs a quick rebase. ✅ |
258543e to
f392148
Compare
f392148 to
627bf7c
Compare
…38) Updates `parity-manifest.json` to reflect the 8-PR parity wave landed today. `commands_covered` block extended with the new commands (now 35 total): - 3 new executions subcommands (replay / verification-pending / verify) from PR #31 - 9 new agents commands (messaging primitive identity surface) from PR #28 - 4 new messages commands (messaging primitive lifecycle) from PR #29 - 2 new workers commands (list / delete) from PR #33 - 2 new `key webhook-secret` subcommands from PR #33 - `--send-at` flag on `cueapi fire` from PR #37 (in flight, depends on hosted #618) `command_drift`: - `cueapi create`: 6 new flags moved missing → covered (--require-payload-override, --required-keys, --delivery, --alerts, --catch-up, --verification, --on-success-fire). Only `--transport` remains in missing — flagged as refactor scope, not a simple flag-add. - `cueapi update`: 9 new flags moved missing → covered. missing_flags now empty. - `cueapi fire`: new entry capturing the 3 covered flags including --send-at. - `cueapi executions get`: missing_display cleared (PR #589 ported in #26). - `cueapi executions list`: 4 new filters moved missing → covered (no remaining gap). `endpoints_missing`: - All 8 entries from the seed manifest cleared except `POST /v1/worker/heartbeat`. - That endpoint stays in missing with documented rationale: cueapi-worker is the canonical wrapper with proper heartbeat-loop semantics. Direct CLI registration is redundant. Decision documented in the manifest entry + cueapi workers group docstring. `ported_pr_history`: - 9 entries with merge timestamps, replacing the pre-port forward-looking notes. - Each entry cross-references the cueapi-cli PR + the hosted PR (when applicable) + the merge timestamp for forensics. `cli_version_at_audit`: bumped 0.1.x → 0.2.x to reflect the new surface area. `last_full_audit`: kept at 2026-05-04 (this update is the post-merge snapshot of the 2026-05-04 audit, not a fresh audit). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…2 + #683 parity) (#43) Extends the existing fire command with two flags covering recently-shipped server features. --send-at was already there from a prior port (#618). --exit-criteria (repeatable, max 20) §14 work-verification-light required-assertion keys (cueapi #632). Receiver MUST report values for every key under outcome.assertions; missing keys mark the execution verification_failed. Pass empty string '' once to opt out of cue-level required_assertions for this fire (server distinguishes [] = explicit opt-out from None = use cue-level). --idempotency-key (≤256 chars) Opaque dedup key (cueapi #683 Phase 2). Same key + same body within 24h returns the cached execution; same key + different body returns 409 idempotency_key_conflict. Implementation pin (caught earlier on cueapi-python #33 + cueapi-mcp #29): idempotency_key is a BODY field on cues fire, NOT the Idempotency-Key header. Server's FireRequest schema diverges from the messaging primitive's header-based idempotency. Help text + load-bearing test pin this so a future refactor doesn't 'simplify' to header-based (which would silently not work — server FireRequest is extra='forbid' for unknown body fields, ignores headers in body parsing). Tests added (3): - test_fire_help: pins all 5 flags appear in --help (--payload-override, --merge-strategy, --send-at, --exit-criteria, --idempotency-key) - test_fire_exit_criteria_repeatable_via_help: validates click parser accepts repeated --exit-criteria flags - test_fire_help_pins_idempotency_key_as_body_field: load-bearing pin against future header-refactor 185/185 tests pass (was 183; 2 new + parser-validation added inline). Source: drift audit handoff/cueapi-package-drift-2026-05-06; Backlog rows "Parity port: PR #632 → cueapi-cli" + "PR #683 not in backlog (folded in same-day)". Triggered by cueapi-main confirming bandwidth on cueapi-cli lane this session ([CC-CUEAPI-DOC-DEBT-CLOSED-DISCIPLINE- CODIFIED]). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Second half of the messaging-primitive CLI port. Pairs with PR #28 (
cueapi agentscommand group) to close the entireMessaging primitivegap from cueapi-cli #25'sparity-manifest.json.PR #28 covers identity + inbox + sent. This PR covers the per-message lifecycle (send / get / read / ack).
Commands shipped (4)
cueapi messages sendX-Cueapi-From-Agentheader (not body); idempotency viaIdempotency-Keyheader; renders dedup-hit explicitly; surfacesX-CueAPI-Priority-Downgraded:true; helpful 409 copy on Idempotency-Key conflictcueapi messages getcueapi messages readcueapi messages ackDesign notes worth flagging
Sender goes via
X-Cueapi-From-AgentHEADER, not in the body. This matches the server's contract (app/routers/messages.pyreads it asHeader(default=None, alias=\"X-Cueapi-From-Agent\")). Pinned bytest_messages_send_minimal_body_and_from_headerso a refactor puttingfromin the body would break the test loudly. Without that test, the refactor would technically still work (server returns 400 via Pydanticextra=\"forbid\") but the failure mode would be silent at unit-test time and only loud at integration time.--expects-replyis a flag — only sent when True. Same omit-when-default pattern as--include-deleted(PR feat: addcueapi agentscommand group (messaging primitive — identity + inbox + sent) #28) and--has-evidence(PR feat: add executions-list filters — outcome-state / result-type / has-evidence / triggered-by #27). Pinned:test_messages_send_omits_expects_reply_when_unset.--priorityvalidated client-side viaclick.IntRange(1, 5). Bad values fail fast without an HTTP round-trip. Server's bound is the same.--idempotency-keymax length 255 enforced client-side with a clean error message before any HTTP call. Matches the server's hard limit (if idempotency_key and len(idempotency_key) > 255: 400).Dedup-hit UI: server returns HTTP 200 (not 201) when an Idempotency-Key + body match an existing message within 24h. The CLI explicitly says "Idempotency-Key dedup hit: existing message returned" so the user doesn't think a fresh send happened. Pinned:
test_messages_send_dedup_hit_renders_existing_label.Priority-downgrade signal surfaced: server may downgrade priority>3 → 3 under receiver-pair limits and signals via
X-CueAPI-Priority-Downgraded: true. CLI shows this so users adapt without parsing body. Pinned:test_messages_send_priority_downgrade_header_surfaced.409 Idempotency-Key conflict copy explicitly tells the user what to do: "reuse the original body or change the key." Better than the generic 409 status string.
Tests
18 new (36 → 54 total). Mock-based body / header / status capture, including a fake
headersfield on_FakeRespso the priority-downgrade test can verify the CLI reads response headers correctly. Pinned behaviors:--fromlands inX-Cueapi-From-Agentheader, NOT body--expects-replyonly sent when True--priorityrejected by Click for out-of-range values--idempotency-keylength limit enforced client-sideAll 54 pass locally.
No hosted-PR dependency
All 4 endpoints already shipped on prod via Phase 12.1 messaging primitive (per MEMORY.md). Pure CLI catch-up.
Test plan
python3 -m pytest tests/test_cli.py -q→ 54 passedcueapi agentscommand group (messaging primitive — identity + inbox + sent) #28 and this land:cueapi agents createcueapi messages send --from <a> --to <b> --body \"hi\" --idempotency-key k1→ 201--body+ same key → 409 with helpful copycueapi messages get <id>→ renders body + metadatacueapi messages read <id>→ 200; second call → 200 unchangedcueapi messages ack <id>→ terminal stateParity Impact
Closes the
messagesportion ofendpoints_missing.Messaging primitivefrom cueapi-cli #25'sparity-manifest.json. Combined with PR #28 (agents portion), the entire messaging-primitive entry is closed. Manifest update follows after #25 merges to main.Companion PR
#28 —
cueapi agentscommand group. Both PRs are independent (different cli.py sections, different test classes) and can land in either order.🤖 Generated with Claude Code